using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Sockets;
using SpoonStun.Handler;
using SpoonStun.Message;

namespace SpoonStun
{
	internal class StunHandler
	{
		private SortedList<int, IHandlerItem> _handlerList = new SortedList<int, IHandlerItem>();
		private StunServer _server;

		public StunHandler(StunServer server)
		{
			_server = server;

			LoadDefault();
		}

		public StunServer Server
		{
			get { return _server; }
		}

		private void LoadDefault()
		{
			
		}

		public void RegisterHandler<T>(OnReceiveMessageHandler<T> handler) where T : IStunMessage, new()
		{
			T type = new T();
			_handlerList.Add(type.MsgId, new HandlerItem<T>(handler));
		}

		public void ReceiveUdpMessage(IPEndPoint point, byte[] msg)
		{
			MemoryStream stream = new MemoryStream(msg);
			BinaryReader br = new BinaryReader(stream);

			int msgId = br.ReadInt32();
			if (_handlerList.ContainsKey(msgId))
			{
				_handlerList[msgId].ReceiveMessage(this, point, br.ReadBytes((int) (stream.Length - stream.Position)));
			}
		}

		private interface IHandlerItem
		{
			void ReceiveMessage(StunHandler handler, IPEndPoint client, byte[] data);
		}

		private class HandlerItem<T> : IHandlerItem where T : IStunMessage, new()
		{
			private OnReceiveMessageHandler<T> _handler;

			public HandlerItem(OnReceiveMessageHandler<T> handler)
			{
				_handler = handler;
			}

			public void ReceiveMessage(StunHandler handler, IPEndPoint client, byte[] data)
			{
				T msg = new T();
				msg.SetBytes(data);
				if (_handler != null) _handler(handler, client, msg);
			}
		}

		public void Send(IPEndPoint client, IStunMessage msg)
		{
			MemoryStream stream = new MemoryStream();
			BinaryWriter bw = new BinaryWriter(stream);

			bw.Write(msg.MsgId);
			bw.Write(msg.GetBytes());

			_server.SendTo(stream.ToArray(), client);
		}
	}

	internal delegate void OnReceiveMessageHandler<T>(StunHandler handler, IPEndPoint client, T msg) where T : IStunMessage, new();
}